package org.boz.dial;

public class LispProcedure extends LispObject {
	private LispObject m_args;
	private LispObject m_body;
	private LispObject m_env;

	private LispObject extend(LispObject env, LispObject sym, LispObject val) {
		return new LispCons(new LispCons(sym, val), env);
	}

	private LispObject multiple_extend(LispObject env, LispObject syms,
			LispObject vals) {
		if (syms == LispPrimitives.nil)
			return env;
		return multiple_extend(extend(env, syms.car(), vals.car()), syms.cdr(),
				vals.cdr());
	}

	public LispProcedure(LispObject args, LispObject body, LispObject env) {
		m_args = args;
		m_body = body;
		m_env = env;
	}

	public String toString() {
		return "#<PROC>";
	}

	public LispObject car() {
		return m_args;
	}

	public LispObject cdr() {
		return m_body;
	}

	public LispObject eval(LispObject env) {
		return this;
	}

	public LispObject apply(LispObject vals, LispObject env) {
		env = multiple_extend(m_env, m_args, vals);
		// Eval expressions
		LispObject exp;
		for (exp = m_body; exp.cdr() != LispPrimitives.nil; exp = exp.cdr()) {
			exp.car().eval(env);
			// System.out.println("exp: " + exp);
			// System.out.println("exp eval'ed to: " + exp.car().eval(env));
		}
		// Implement tail call elimination here:
		LispObject last = exp.car();
		if (last instanceof LispCons) {
			return new LispLast(last, env);
		} else {
			return last.eval(env);
		}
	}
}
